package com.gdcnano.nanoamp.media.sound.playlist
{
	import com.gdcnano.libs.xspf4x.XSPF4X;
	import com.gdcnano.nanoamp.media.sound.SoundManager;
	
	import flash.errors.IllegalOperationError;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	
	import mx.collections.ArrayCollection;
	
    
    /**
	 * Playlist manager class.
	 * 
	 * @author gareevm
	 * 
	 */
    public class PlaylistManager extends EventDispatcher
    {
		private static const TRACKLIST_LOADED:String = "tracklistLoadedEvent";
		
		private static const PLAYLIST_LOADED:String = "playlistLoaded";
		
		private static const UPDATE_PLAYLIST:String = "updatePlaylist";
		
		private static const UPDATE_TRACK_POSITION:String = "updateTrackPosition";
		
		/*
			Events
		*/
		public static const EV_UNKNOWN_PLAYLIST_TYPE:String = "unknownPlaylistType";
		
		public static const EV_PLAYLIST_CREATION_ERROR:String = "playlistCreationError";
		
		/**
		 * Singleton instance of PlaylistManager
		 * @see #getInstance() how get an instance
		 * @see #PlaylistManager() how _instance instantiates
		 */		
		private static var _instance:PlaylistManager;
        
		/**
		 * Loaded playlsit 
		 */        
		private var _playlist:IPlaylist;
		
		private var _playlistType:String;
		
		/**
		 * Sound manager.
		 * Responsible to all operations with sound. Such as play/stop sound.
		 */		
		private var _soundManager:SoundManager;
		
		[Bindable]
		/**
		 * Returns soundManager instance 
		 * @return soundManager
		 * 
		 */		
		public function get soundManager():SoundManager
		{
			return _soundManager;
		}
		/**
		 * Does nothing. Only for Flex binding porposes. 
		 * @param value
		 * 
		 */		
		public function set soundManager(value:SoundManager):void {}
		
		
		[Bindable(event="updatePlaylist")]
		/**
		 * Loaded playlist 
		 * @return loaded playlist 
		 * 
		 */		
		public function get playlist():IPlaylist
		{
			return _playlist;
		}
		
		/**
		 * Does nothing. Used only for binding.
		 * @see #loadPlaylist() to set playlist
		 * @param value IPlaylist
		 * 
		 */		
		public function set playlist(value:IPlaylist):void {}

		
		/**
		 * Returns instance of singleton object 
		 * @return PlaylistManager
		 * 
		 */
		public static function getInstance():PlaylistManager
		{
			if (PlaylistManager._instance == null)
			{
				PlaylistManager._instance = new PlaylistManager(new PlaylistManagerInitClass());
			}
			return PlaylistManager._instance;
		}

	
		/**
		 * Constructor. As class is a singletone, then constructor designed 
		 * to be instantiate only inside class. An instance of class can be got 
		 * from calling static method #getInstance()
		 * @param init internal class type to avoid instantiate singleton outside class.
		 * @see #getInstance() returns instance of PlaylistManager
		 * 
		 */
        public function PlaylistManager(init:PlaylistManagerInitClass)
        {
			_soundManager = SoundManager.getInstance();
			addSoundManagerListeners(soundManager);
        }
        
		
        
        /**
         * Loads playlist from the specified URL
         * @param playlistURL Link to the playlist location
         *
         */
        public function loadPlaylist(playlistURL:String, playlistType:String):void
        {
            var urlLoader:URLLoader = new URLLoader();
            var urlRequest:URLRequest = new URLRequest(playlistURL);
			_playlistType = playlistType;
            urlLoader.addEventListener(Event.COMPLETE, playlistLoadHandler);
            urlLoader.addEventListener(IOErrorEvent.IO_ERROR, loadPlaylistErrorHandler);
            urlLoader.load(urlRequest);
        }
        
		/**
		 * Stops playing sound 
		 * 
		 */		
		public function stopPlay():void
		{
			_soundManager.stopPlay();
		}
        
		/**
		 * Add listeners to soundManager events 
		 * 
		 */		
		private function addSoundManagerListeners(soundManager:SoundManager):void
		{
			soundManager.addEventListener(SoundManager.TRACK_PLAYING_COMPLETE, trackPlayingCompleteHandler)
		}
		
		private function addPlaylistListeners(playlist:IPlaylist):void
		{
			(playlist as AbstractPlaylist).addEventListener(AbstractPlaylist.TRACK_CHANGED, trackChanghedHandler);
			(playlist as AbstractPlaylist).addEventListener(AbstractPlaylist.TRACK_IS_EMPTY, trackClearedHandler);
		}
		
		/*-------------------------
			HANDLERS
		-------------------------*/
		/**
		 * Playlist load error event handler
		 * @param evner
		 *
		 */
		private function loadPlaylistErrorHandler(evner:IOErrorEvent):void
		{
			//TODO: dispatch error event
		}
		
		/**
		 * Playlist load complere event handler
		 * @param event
		 *
		 */
		private function playlistLoadHandler(event:Event):void
		{
			stopPlay();
			
			_playlist = PlaylistFactory.createPlaylist(_playlistType);
			
			if (!_playlist.setPlaylistData(event.target.data))
			{
				dispatchEvent(new Event(EV_PLAYLIST_CREATION_ERROR));
				return;
			}
			
			addPlaylistListeners(_playlist);
			
			dispatchEvent(new Event(UPDATE_PLAYLIST));
		}
		
		private function trackChanghedHandler(event:Event):void
		{
			_soundManager.loadAndPlayTrack(playlist.currentTrack);
			
			dispatchEvent(new Event(UPDATE_PLAYLIST));
		}
		
		private function trackClearedHandler(event:Event):void
		{
			stopPlay();
		}
		
		/**
		 * Handles track play complete event from soundManager 
		 * and tries to play next track if available
		 * @param event SoundManager.TRACK_PLAYING_COMPLETE event
		 * @see com.gdcnano.nanoamp.media.sound.SoundManager.#soundCompleteEventHandler()
		 */		
		private function trackPlayingCompleteHandler(event:Event):void
		{
			playlist.nextTrack();
		}
    }
}

internal class PlaylistManagerInitClass
{
	public function PlaylistManagerInitClass()
	{
		
	}
}